iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0
自我挑戰組

從 Python 開發者的角度學習 Rust —— 從語法基礎到實戰應用系列 第 27

[Day 27] Rust 的 Web 應用(四):Rust + React 全端開發

  • 分享至 

  • xImage
  •  

在前一篇文章當中我們展示了如何使用 Rust 當中的 Rocket 以及 mongoDB 套件,建立了一個簡單的使用者資料管理方面的 RESTful API,但是總不能叫使用者只能透過 API 直接與資料庫溝通吧,這樣的話就不叫作 Web 應用了,當然還需要有前端作為使用者的操作介面囉,這時候我們就可以與之前提到的前端框架 React 進行結合,並且只需要簡單的幾個步驟即可完成這樣的全端開發。

一、Rust + React 專案的檔案結構

1. 建立 React App

為了要沿用上一篇的專案內容的 RESTful API 作為資料庫互動的後端,我們現在需要在既有的檔案結構下新增 React 專案,假設上一篇的檔案結構最後長這樣, rust-restful-api 專案是被放在 myproject 內:

myproject/
└── rust-restful-api/
    ├── src/
    │   └── main.rs
    ├── target/
    ├── .gitignore
    ├── Cargo.lock
    └── Cargo.toml

那我們現在可以在 myproject 路徑下在終端機輸入指令

npx create-react-app frontend

這樣就會建立一個名為 frontend 的前端專用資料夾,接著我們直接進入該資料夾,執行打包的指令

cd  frontend
npm run build

完成之後檔案結構會長這樣

myproject/
├── frontend/
│   ├── build/
│   ├── node_modules/
│   ├── public/
│   ├── src/
│   ├── .gitignore
│   ├── package-lock.json
│   ├── package.json
│   └── README.md
└── rust-restful-api/
    ├── src/
    └── target/
    └──.gitignore
    └──Cargo.lock
    └──Cargo.toml

接下來,我們要先處理 Rust 這邊讀取前端網頁的程式碼

2. 首頁指向 React 網頁

首先,修改一下 Rust 的 main.rs,我們直接看修改後的完整程式碼,基於上一篇的範例修改的

use futures::stream::TryStreamExt; // 導入 TryStreamExt,提供 `try_collect` 方法以簡化處理 MongoDB 查詢結果
use mongodb::{bson::doc, bson::oid::ObjectId, options::ClientOptions, Client}; // 導入 MongoDB 客戶端、選項及 BSON 文檔的處理工具
use rocket::{delete, fs::FileServer, get, post, put, routes, serde::json::Json, State}; // 導入 Rocket 框架的路由、HTTP 請求方法及狀態管理
use serde::{Deserialize, Serialize}; // 導入 Serde 序列化及反序列化,讓結構體能夠轉換成 JSON

// 定義一個用戶結構,支援 JSON 的序列化和反序列化,並實現 Debug 和 Clone 特徵
#[derive(Debug, Deserialize, Serialize, Clone)]
struct User {
    #[serde(skip_serializing_if = "Option::is_none")] // 如果 id 欄位是 None,序列化時會跳過該欄位
    id: Option<String>, // 用戶的 ID,可能不存在(可選)
    name: String, // 用戶的名字,為必填欄位
}

// Rocket 主程式入口,使用 async 函數以支援異步操作
#[rocket::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 設置 MongoDB 連接選項,使用 MongoDB Atlas 提供的 URL 進行連接
    let client_options = ClientOptions::parse("mongodb+srv://masoufo0310:2ioxaagu@database.541vj.mongodb.net/?retryWrites=true&w=majority&appName=Database").await?;
    let client = Client::with_options(client_options)?; // 建立 MongoDB 客戶端連接

    let _db = client.database("testdb"); // 取得指定的資料庫 testdb

    // 構建 Rocket 應用,並管理 MongoDB 客戶端狀態
    rocket::build()
        .manage(client) // 傳遞 MongoDB 客戶端到 Rocket 管理狀態
        .mount(
            // 註冊路由,將所有 CRUD 操作綁定到應用上
            "/",
            routes![create_user, get_users, update_user, delete_user], // 綁定路由,處理用戶的新增、查詢、更新、刪除
        )
        .mount("/", FileServer::from("../frontend/build")) // 讓 Rocket 提供 React build 目錄中的靜態資源
        .ignite() // 啟動 Rocket 應用,初始化伺服器
        .await?
        .launch() // 啟動伺服器,並開始監聽請求
        .await?;

    Ok(()) // 正常退出時返回 OK
}

// POST 請求,用來新增用戶
#[post("/users", data = "<user>")]
async fn create_user(client: &State<Client>, user: Json<User>) -> Json<User> {
    let collection = client.database("testdb").collection::<User>("users"); // 連接到 MongoDB 的 testdb 資料庫,並選擇 users 集合
    let new_user = User {
        id: None,                // 新增用戶時,ID 設為 None,由 MongoDB 自動生成
        name: user.name.clone(), // 複製請求中的用戶名稱
    };

    collection.insert_one(new_user.clone(), None).await.unwrap(); // 將新用戶資料插入資料庫,忽略錯誤處理
    Json(new_user) // 回傳新增的用戶資料作為 JSON
}

// GET 請求,用來獲取所有用戶資料
#[get("/users")]
async fn get_users(client: &State<Client>) -> Json<Vec<User>> {
    let collection = client.database("testdb").collection::<User>("users"); // 連接到 MongoDB 的 testdb 資料庫,並選擇 users 集合

    let cursor = collection.find(None, None).await.unwrap(); // 從集合中查詢所有用戶,獲取 MongoDB 游標
    let users: Vec<User> = cursor.try_collect().await.unwrap(); // 將游標中的用戶資料轉換為 Vec<User>

    Json(users) // 回傳所有用戶的 JSON 數據
}

// PUT 請求,用來更新指定 ID 的用戶資料
#[put("/users/<id>", data = "<user>")]
async fn update_user(client: &State<Client>, id: String, user: Json<User>) -> Json<User> {
    let collection = client.database("testdb").collection::<User>("users");

    // 將 id 轉換為 ObjectId
    let object_id = match ObjectId::parse_str(&id) {
        Ok(oid) => oid,
        Err(_) => {
            return Json(User {
                id: Some(id),
                name: "Invalid ID format".to_string(),
            })
        }
    };

    let filter = doc! { "_id": object_id }; // 根據 ObjectId 進行過濾
    let update = doc! { "$set": { "name": &user.name } }; // 設置更新條件

    collection.update_one(filter, update, None).await.unwrap();
    Json(User {
        id: Some(id),            // 更新後的用戶 ID 設為指定的 ID
        name: user.name.clone(), // 返回更新後的用戶名字
    })
}

// DELETE 請求,用來刪除指定 ID 的用戶
#[delete("/users/<id>")]
async fn delete_user(client: &State<Client>, id: String) -> String {
    let collection = client.database("testdb").collection::<User>("users"); // 連接到 MongoDB 的 testdb 資料庫,並選擇 users 集合

    // 將傳入的 id 轉換為 ObjectId,如果失敗則回傳錯誤訊息
    let object_id = match ObjectId::parse_str(&id) {
        Ok(oid) => oid,
        Err(_) => return format!("Invalid ID format: {}", id),
    };

    let filter = doc! { "_id": object_id }; // 使用 BSON 格式,根據 ObjectId 進行過濾

    // 執行刪除操作,忽略錯誤處理
    match collection.delete_one(filter, None).await {
        Ok(result) => {
            if result.deleted_count == 1 {
                format!("User with id {} deleted", id) // 回傳刪除成功訊息
            } else {
                format!("No user found with id {}", id) // 如果沒有找到用戶,回傳未找到訊息
            }
        }
        Err(_) => format!("Failed to delete user with id {}", id), // 刪除失敗時回傳錯誤訊息
    }
}

這段程式碼 98% 都跟上一篇的一模一樣,只有少少的新增了兩行:

  1. use rocket::{delete, fs::FileServer, get, post, put, routes, serde::json::Json, State}; ,在這當中加入了 fs::FileServer,它的作用是讓 Rocket 提供靜態資源。這意味著,我們可以指向一個特定的目錄,Rocket 會將該目錄中的檔案(如 HTML、JavaScript、CSS 等)直接提供給用戶端。在這個案例中,我們將 React 前端應用打包後的靜態檔案放在 ../frontend/build 目錄中,而 FileServer 就是負責將這些靜態檔案提供給用戶。
  2. .mount("/", FileServer::from("../frontend/build")) ,在 rocket::build 內加入了當使用者進入 / 路徑時,以 ../frontend/build 作為 FileServer 他就是預設將該路徑資料夾中的 index.html 作為回傳的檔案內容,並且也能讓用戶讀取得到裡面的 static 靜態資源。

實際執行的時間到了

cargo run

執行後可以看到以下畫面出現在 http://127.0.0.1:8000

https://ithelp.ithome.com.tw/upload/images/20241010/20121176raAQVS0YkN.png

就代表我們已經透過 rocket 完整的結合了 React 跟 Rust 的前後端,接下來讓我們完成前端用戶介面吧

3. 建立用戶資料管理介面

現在我們可以再開一個終端機,以區別原本的後端所使用的終端機,並且先進入 React資料夾下

cd frontend

我們會需要透過前端去呼叫 API ,所以這邊先用指令安裝一下 React 常用的 request 套件 axios

npm install axios

完成套件安裝之後,我們需要修改兩個檔案,首先第一個檔案是位於 src 之下的 App.js,他原本的內容應該是長這樣的

import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

可能有人對於 React 還並不是很認識,但我們就先略過關於這段程式碼當中的介紹,先以概念與流程為主,簡單來說,這個 App.js 的內容就是我們剛剛上面網頁中所呈現的內容主體,因此我們只需要修改這個 App.js 就可以重新建立我們的首頁畫面,接下來我們就直接進行修改,修改後的 App.js 內容如下:

import { useState, useEffect } from 'react'; // 從 React 中導入 useState 和 useEffect,用來管理狀態與副作用
import axios from 'axios'; // 從 axios 套件導入,用來處理 HTTP 請求
import './App.css'; // 導入外部的 CSS 檔案,設置樣式

function App() {
  const [users, setUsers] = useState([]); // 定義一個狀態變數 users,用來儲存用戶列表,初始值為空陣列
  const [newUserName, setNewUserName] = useState(''); // 定義一個狀態變數 newUserName,用來儲存新增用戶的名字,初始值為空字串
  const [updateUserName, setUpdateUserName] = useState(''); // 定義一個狀態變數 updateUserName,用來儲存更新用戶的名字,初始值為空字串
  const [userIdToUpdate, setUserIdToUpdate] = useState(''); // 定義一個狀態變數 userIdToUpdate,用來儲存需要更新的用戶 ID,初始值為空字串

  // 在組件首次渲染後,執行獲取用戶列表的函數
  useEffect(() => {
    fetchUsers(); // 執行獲取用戶的函數
  }, []); // 這裡的空陣列表示只在組件首次載入時執行一次

  // 獲取用戶列表的非同步函數
  const fetchUsers = async () => {
    try {
      const response = await axios.get('/users'); // 向後端發送 GET 請求,獲取用戶資料
      setUsers(response.data); // 將獲取到的用戶資料設定到 users 狀態中
    } catch (error) {
      console.error('Error fetching users:', error); // 如果發生錯誤,顯示錯誤訊息
    }
  };

  // 新增用戶的非同步函數
  const createUser = async () => {
    try {
      if (!newUserName) return; // 如果沒有輸入用戶名稱,則不執行新增操作
      await axios.post('/users', { name: newUserName }); // 向後端發送 POST 請求,傳送新用戶資料
      setNewUserName(''); // 清空輸入框的內容
      fetchUsers(); // 重新獲取用戶列表,更新畫面
    } catch (error) {
      console.error('Error creating user:', error); // 如果發生錯誤,顯示錯誤訊息
    }
  };

  // 刪除用戶的非同步函數
  const deleteUser = async (id) => {
    try {
      await axios.delete(`/users/${id}`); // 向後端發送 DELETE 請求,刪除指定 ID 的用戶
      fetchUsers(); // 重新獲取用戶列表,更新畫面
    } catch (error) {
      console.error('Error deleting user:', error); // 如果發生錯誤,顯示錯誤訊息
    }
  };

  // 更新用戶的非同步函數
  const updateUser = async () => {
    try {
      if (!userIdToUpdate || !updateUserName) return; // 如果沒有輸入要更新的用戶 ID 或新名稱,則不執行更新操作
      await axios.put(`/users/${userIdToUpdate}`, { name: updateUserName }); // 向後端發送 PUT 請求,更新指定 ID 的用戶資料
      setUserIdToUpdate(''); // 清空輸入框的內容
      setUpdateUserName(''); // 清空輸入框的內容
      fetchUsers(); // 重新獲取用戶列表,更新畫面
    } catch (error) {
      console.error('Error updating user:', error); // 如果發生錯誤,顯示錯誤訊息
    }
  };

  return (
    <div className="App"> {/* 最外層的 div,套用 CSS 樣式 App */}
      <header className="App-header"> {/* 頁面的 header,套用 CSS 樣式 App-header */}
        <h1>會員資料管理系統</h1> {/* 頁面標題 */}

        <div className="form-section"> {/* 用於表單的區塊,包含新增和更新會員 */}
          <div className="form-group"> {/* 表單組塊,用於新增會員 */}
            <h2>新增會員</h2> {/* 小標題:新增會員 */}
            <input
              type="text"
              value={newUserName} // 綁定新用戶名稱的狀態變數
              onChange={(e) => setNewUserName(e.target.value)} // 當輸入變更時,更新 newUserName 狀態
              placeholder="輸入會員名稱" // 提示使用者輸入會員名稱
            />
            <button onClick={createUser}>新增會員</button> {/* 點擊按鈕時執行 createUser 函數 */}
          </div>

          <div className="form-group"> {/* 表單組塊,用於更新會員 */}
            <h2>更新會員</h2> {/* 小標題:更新會員 */}
            <input
              type="text"
              value={userIdToUpdate} // 綁定需要更新的會員 ID 的狀態變數
              onChange={(e) => setUserIdToUpdate(e.target.value)} // 當輸入變更時,更新 userIdToUpdate 狀態
              placeholder="輸入會員ID" // 提示使用者輸入會員 ID
            />
            <input
              type="text"
              value={updateUserName} // 綁定新的會員名稱的狀態變數
              onChange={(e) => setUpdateUserName(e.target.value)} // 當輸入變更時,更新 updateUserName 狀態
              placeholder="輸入新的會員名稱" // 提示使用者輸入新的會員名稱
            />
            <button onClick={updateUser}>更新會員</button> {/* 點擊按鈕時執行 updateUser 函數 */}
          </div>
        </div>

        <div className="table-section"> {/* 表格區塊,用於顯示會員列表 */}
          <h2>會員列表</h2> {/* 小標題:會員列表 */}
          <table className="user-table"> {/* 使用者表格,套用 CSS 樣式 user-table */}
            <thead>
              <tr>
                <th>會員ID</th> {/* 表格標題:會員 ID */}
                <th>會員名稱</th> {/* 表格標題:會員名稱 */}
                <th>操作</th> {/* 表格標題:操作 */}
              </tr>
            </thead>
            <tbody>
              {users.map((user) => ( // 遍歷 users 陣列,顯示每個用戶的資料
                <tr key={user.id}> {/* 每一列的唯一 key 為 user.id */}
                  <td>{user.id}</td> {/* 顯示用戶的 ID */}
                  <td>{user.name}</td> {/* 顯示用戶的名稱 */}
                  <td>
                    <button className="edit-btn" onClick={() => deleteUser(user.id)}>刪除</button> {/* 刪除按鈕,點擊後執行 deleteUser 函數 */}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </header>
    </div>
  );
}

export default App; // 導出 App 組件,以便在其他地方使用

修改完畢之後,我們要針對網頁的樣式進一步修改,所以我們也同步修改這個 App.css 的內容如下:

.App {
    text-align: center;
    background-color: #f9f3e7;
    color: #4f4f4f;
    font-family: 'Arial', sans-serif;
}

.App-header {
    background-color: #f3e5d5;
    padding: 20px;
    border-radius: 10px;
}

h1 {
    color: #c48d41;
}

.form-section {
    display: flex;
    justify-content: space-around;
    margin: 20px 0;
}

.form-group {
    background-color: #fff4e6;
    padding: 15px;
    border-radius: 10px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

input {
    margin: 10px;
    padding: 10px;
    border: 2px solid #c48d41;
    border-radius: 5px;
}

button {
    padding: 10px 20px;
    background-color: #e07a5f;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

button:hover {
    background-color: #c5533d;
}

.table-section {
    margin-top: 20px;
}

.user-table {
    width: 80%;
    margin: 0 auto;
    border-collapse: collapse;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.user-table th,
.user-table td {
    padding: 10px 15px;
    border: 1px solid #ddd;
}

.user-table th {
    background-color: #f7d6ad;
    color: #4f4f4f;
}

.user-table td {
    background-color: #fffaf0;
}

.edit-btn {
    background-color: #e07a5f;
    color: white;
    padding: 5px 10px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

.edit-btn:hover {
    background-color: #c5533d;
}

完成之後,我們再次打包,每次前端修改完畢之後,都要重新打包,這樣才能刷新後端那邊讀取到的 build 資料夾內的檔案

npm run build

打包完畢之後,我們再次查看前端網頁現在的樣子,後端這邊也需要重新載入,只需要按下 Ctrl + F5 刷新即可

https://ithelp.ithome.com.tw/upload/images/20241010/201211769xquy1sL51.png

現在我們就可以透過前端介面跟資料庫溝通囉,如果我們要修改網頁內容,就只需要更改 App.js 或者 App.css 之後,用 npm run build 重新打包就可以了,這樣我們就可以同時使用 React 與 Rust 的高效能進行開發了

上面展示了我們將兩者之間結合的結果,現在我們要開始解析 App.js 的網頁內容了

二、關於 App.js 的詳細說明

在這個段落,我們將深入探討 App.js 檔案中的核心概念,這些概念能夠幫助你更好地理解如何透過 React 與後端 API 進行互動,並創建一個有效的前端資料管理介面。

1. 狀態管理 (useState)

首先,我們利用了 React 的 useState 來管理應用的狀態。在 App.js 中,我們使用了四個狀態變數來處理不同的需求:

  • users: 用來存放從後端取得的用戶列表,這是一個陣列,初始值為空陣列。
  • newUserName: 儲存用戶在新增會員時輸入的名稱。
  • updateUserName: 儲存用戶在更新會員時輸入的新的名稱。
  • userIdToUpdate: 儲存用戶在更新會員時輸入的用戶 ID。

這些狀態變數可以透過 setUserssetNewUserNamesetUpdateUserNamesetUserIdToUpdate 來動態更新。這些變數在 React 組件的生命週期內,會根據使用者的操作而改變,並導致畫面重新渲染。

2. 使用 useEffect 來處理資料的初始化載入

useEffect 是 React 中用來處理副作用(side effects)的 Hook,在這個應用中,我們使用 useEffect 來在組件載入時從後端 API 獲取資料。具體來說,我們在 useEffect 中調用了 fetchUsers 函數,這個函數會在組件第一次被載入時自動執行,向後端發送 GET 請求來獲取所有的會員資料。

以下是 useEffect 的簡單工作流程:

useEffect(() => {
  fetchUsers(); // 組件載入後獲取用戶資料
}, []); // 空陣列表示該副作用只在組件首次載入時執行一次

3. 與後端 API 互動

React 前端與後端 API 的互動是透過 axios 這個 HTTP 客戶端庫來實現的。axios 允許我們使用簡單的函數來發送 GET、POST、PUT、DELETE 等 HTTP 請求,並處理回應。在這個應用中,分別有三個主要的操作:

  • 獲取用戶列表 (GET 請求):
    我們使用 axios.get 來向 /users 路徑發送 GET 請求,並將後端回應的用戶資料存入 users 狀態變數中,這樣就能夠在頁面上顯示用戶列表。

    const fetchUsers = async () => {
      try {
        const response = await axios.get('/users'); // 向後端發送 GET 請求
        setUsers(response.data); // 將獲取到的用戶列表設定到狀態中
      } catch (error) {
        console.error('Error fetching users:', error); // 處理錯誤
      }
    };
    
  • 新增用戶 (POST 請求):
    當使用者在表單中輸入會員名稱並點擊 "新增會員" 按鈕後,createUser 函數會發送 POST 請求到 /users,新增一個新的用戶。

    const createUser = async () => {
      try {
        if (!newUserName) return; // 如果沒有輸入用戶名稱,則不執行
        await axios.post('/users', { name: newUserName }); // 發送 POST 請求新增用戶
        setNewUserName(''); // 清空輸入框
        fetchUsers(); // 重新獲取用戶列表
      } catch (error) {
        console.error('Error creating user:', error); // 處理錯誤
      }
    };
    
  • 更新用戶 (PUT 請求):
    當使用者輸入用戶 ID 和新的名稱後,點擊 "更新會員" 按鈕,updateUser 函數會發送 PUT 請求來更新對應的用戶資料。

    const updateUser = async () => {
      try {
        if (!userIdToUpdate || !updateUserName) return; // 檢查是否輸入了用戶 ID 和新名稱
        await axios.put(`/users/${userIdToUpdate}`, { name: updateUserName }); // 發送 PUT 請求更新用戶資料
        setUserIdToUpdate(''); // 清空輸入框
        setUpdateUserName(''); // 清空輸入框
        fetchUsers(); // 重新獲取用戶列表
      } catch (error) {
        console.error('Error updating user:', error); // 處理錯誤
      }
    };
    
  • 刪除用戶 (DELETE 請求):
    當使用者點擊 "刪除" 按鈕後,deleteUser 函數會發送 DELETE 請求,從後端刪除對應的用戶資料。

    const deleteUser = async (id) => {
      try {
        await axios.delete(`/users/${id}`); // 發送 DELETE 請求刪除用戶
        fetchUsers(); // 重新獲取用戶列表
      } catch (error) {
        console.error('Error deleting user:', error); // 處理錯誤
      }
    };
    

4. 表單處理與畫面更新

我們的前端頁面有兩個主要的表單區域,一個用來新增會員,另一個用來更新會員。使用者輸入名稱或 ID 時,這些值會即時存入對應的狀態變數,並在點擊按鈕時觸發相應的函數。

  • 新增會員的表單包括一個輸入框和一個按鈕,輸入框綁定了 newUserName,按鈕則綁定 createUser 函數。
  • 更新會員的表單有兩個輸入框,一個是會員 ID,一個是新的會員名稱,分別綁定 userIdToUpdateupdateUserName,按鈕則綁定 updateUser 函數。

每次新增、更新或刪除會員後,React 組件會自動重新渲染,並顯示最新的用戶列表,這是因為我們在每次操作後都會呼叫 fetchUsers 函數來更新 users 狀態。

5. 用戶列表顯示

在表格區域中,我們使用了 map 函數來遍歷 users 陣列,並生成每個用戶的表格行。對於每個用戶,我們顯示其 ID、名稱,並附上 "刪除" 按鈕。當點擊刪除按鈕時,會呼叫 deleteUser 函數,根據用戶的 ID 將其從資料庫中刪除。

這個 App.js 檔案展示了 React 前端如何透過 API 與後端服務進行互動。React 的 useStateuseEffect Hook 幫助我們簡化了狀態管理和資料載入的邏輯,而 axios 庫則讓我們能夠輕鬆發送 HTTP 請求。在此基礎上,這個用戶管理系統可以進一步擴展,加入更多功能如驗證、分頁、排序等。

總結

這篇文章結合了使用Rust + React作為全端開發的基礎,回顧一開始從 Python 開發者的角度切入,到目前為止已經經歷了27天,對於 Rust 雖然不敢說熟悉,但是也因此增加了很多基礎知識與概念,現在再加上 React 的前端開發進來,可以說是在網頁開發上的一大進展,雖然 Python 的 Django 與 flask 等框架也是提供了非常簡易的網頁開發能力,相較之下 Rust 的 API 撰寫上當然也是比較困難的,但為了使用到 Rust 的高效能,讓它能夠擔任後端處理的工作也是相當不錯的選擇。

React 作為目前最熱門的前端框架之一,自然也是有許多可以適用的套件能供開發者選擇,由於今天的文章當中僅以 App.js 單一檔案為範例,再接下來,我們需要補充最後一個Web應用開發必要的部分,來解釋如果我的網頁需要有多個分頁,則應該怎麼去修改,畢竟總不能只使用一個分頁就完成整個網站的開發吧

也趁這個機會,下一篇我們將會再次說明 React 網頁開發的方式是怎麼一回事,希望也能提供給對 React 不熟悉的人有更進一步的認識囉


上一篇
[Day 26] Rust 的 Web 應用(三):使用 Rocket 與 MongoDB 建立 RESTful API
下一篇
[Day 28] Rust 的 Web 應用(五):React 分頁管理
系列文
從 Python 開發者的角度學習 Rust —— 從語法基礎到實戰應用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言